package io.renren.controller;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Maps;
import com.vortex.tool.httpclient.HttpMethodName;
import com.vortex.tool.httpclient.Protocol;
import com.vortex.tool.httpclient.VtxHttpClient;
import com.vortex.tool.httpclient.request.VtxHttpRequest;
import com.vortex.tool.httpclient.sign.VtxV1Signer;
import io.renren.common.utils.MD5Util;
import io.renren.common.utils.R;
import io.renren.common.utils.RequestDataExtractor;
import io.renren.common.utils.Sign;
import io.renren.constant.DeviceFinal;
import io.renren.modules.generator.controller.vo.AuthVo;
import io.renren.modules.generator.entity.EnterpriseMainEntity;
import io.renren.modules.generator.entity.EnterpriseThirdBindRecordEntity;
import io.renren.modules.generator.service.EnterpriseMainService;
import io.renren.modules.generator.service.EnterpriseThirdBindRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.net.ssl.SSLContext;
import javax.validation.Valid;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Map;

/**
 * 公共
 *
 * @author chenshun
 * @email sunlightcs@gmail.com
 * @date 2020-08-25 14:30:05
 */
@Validated
@RestController
@RequestMapping("common/device")
@Api("公共接口")
public class CommonController extends BaseController{

    private static Logger logger = LoggerFactory.getLogger(CommonController.class);

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private EnterpriseThirdBindRecordService enterpriseThirdBindRecordService;

    @Value("${file.docBase:}")
    private String docBase;

    @Value("${file.path:}")
    private String filePath;

    @Value("${oauth2.clientId}")
    private String clientId;

    @Value("${oauth2.clientSecret}")
    private String clientSecret;

    @Value("${oauth2.tokenUrl}")
    private String tokenUrl;

    @Value("${oauth2.userInfoUrl}")
    private String userInfoUrl;

    @Autowired
    private EnterpriseMainService enterpriseMainService;

    /**
     * 列表
     */
    @RequestMapping("/codelist")
    @ApiOperation("指标数据获取")
    public R chartlist(@RequestParam(value = "type",required = false,defaultValue = "") String type) {
        if (StringUtils.isNotBlank(type)) {
            return R.ok().put("codelist", DeviceFinal.deviceCodeMap.get(type));
        } else {
            return R.ok().put("codelist", DeviceFinal.deviceCodeMap);
        }
    }

    @PostMapping("/token")
    @ApiOperation("认证JWT获取")
    public R token(@Valid @RequestBody AuthVo vo) throws Exception {
        boolean success;
        //根据用户名密码验证账号是否正确
        Map<String, String> params;
        ResponseEntity<Map> response;
        String inputLine;
        CloseableHttpClient closeableHttpClient = getIgnoeSSLClient();
        //使用{}占位符，后面跟Object类型参数，这里使用Map<String,String>类型，里面存入了depotId

        HttpGet httpGet = new HttpGet(org.apache.commons.lang3.StringUtils.join(tokenUrl,"?grant_type=password&username=",
                vo.getUserNo(),"&password=",MD5Util.getMD5Str(vo.getPassword()),"&client_id=",clientId,"&client_secret=",clientSecret));
        CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpGet);
        logger.error("========================closeableHttpResponse.url:"+tokenUrl+"?grant_type=password&username="+
                vo.getUserNo()+"&password="+MD5Util.getMD5Str(vo.getPassword())+"&client_id="+clientId+"&client_secret="+clientSecret);
        logger.info("========================closeableHttpResponse.getStatusLine().getStatusCode():"+closeableHttpResponse.getStatusLine().getStatusCode());
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                 closeableHttpResponse.getEntity().getContent()));
        StringBuffer response1 = new StringBuffer();
        while ((inputLine = reader.readLine()) != null) {
            response1.append(inputLine);
        }
        reader.close();
        if (closeableHttpResponse.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
            return R.error("认证异常:" + response1.toString());
        }
        closeableHttpClient.close();
        //通过jscode获取openId
        WxMaJscode2SessionResult wxMaJscode2SessionResult = null;
        try {
            wxMaJscode2SessionResult = getWxMaService().jsCode2SessionInfo(vo.getJscode());
        } catch (WxErrorException errorException) {
            return R.error("微信认证异常:" + errorException.getError().getErrorCode() + " " + errorException.getError().getErrorMsg());
        }
        String openId = wxMaJscode2SessionResult.getOpenid();
        if (StringUtils.isBlank(openId)) {
            return R.error("获取openId为空!");
        }
        //根据用户id和站点id获取用户相关信息 包含部门和角色
        params = Maps.newLinkedHashMap();
        params.put("userNo",vo.getUserNo());
        //使用{}占位符，后面跟Object类型参数，这里使用Map<String,String>类型，里面存入了depotId
        response = this.restTemplate.getForEntity(userInfoUrl + "&userNo={userNo}",
                Map.class, params);
        if (response.getStatusCode() != HttpStatus.OK) {
            return R.error(response.getBody().get("content").toString());
        }
        //根据结果封装token
        EnterpriseThirdBindRecordEntity userDto = process(response.getBody());
        if (StringUtils.isBlank(userDto.getDeptId())) {
            return R.error("当前用户在认证中对应的deptId获取为空！");
        }
        if (userDto.getEnterpriseId() == null) {
            logger.info("当前用户部门编号为{},部门名称为{},不属于具体企业！", userDto.getDeptId(), userDto.getDeptName());
        }
        if (StringUtils.isBlank(userDto.getRoleCode()) || StringUtils.isBlank(userDto.getRoleName())) {
            return R.error("当前用户未分配角色，请联系管理员！");
        }
        String token = Sign.generateSessionToken(userDto, "secret", 720 * 60 * 60 * 1000L);
        //保存绑定信息到数据库中
        userDto.setOpenid(openId);
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_no",userDto.getUserNo());
        queryWrapper.eq("user_name",userDto.getUserName());
        int count = getEnterpriseThirdBindRecordService().count(queryWrapper);
        if (count == 0) {
            userDto.setCreateUserId(userDto.getUserNo());
            userDto.setCreateUserName(userDto.getUserName());
            success = getEnterpriseThirdBindRecordService().save(userDto);
        } else {
            userDto.setUpdateTime(new Date());
            userDto.setUpdateUserId(userDto.getUserNo());
            userDto.setUpdateUserName(userDto.getUserName());
            success = getEnterpriseThirdBindRecordService().updateByUserNo(userDto);
        }
        if (!success) {
            return R.error("用户绑定失败！");
        }
        return R.ok().put("userNo", userDto.getUserNo()).put("token",token).put("roleCode", userDto.getRoleCode()).put("roleName",userDto.getRoleName())
                .put("deptId",userDto.getDeptId()).put("deptName",userDto.getDeptName()).put("enterpriseId", userDto.getEnterpriseId());
    }

    @GetMapping("/token/validate")
    @ApiOperation("认证Token有效性校验")
    public R tokenValidate(String userNo) {
        //从请求头中获取token
        String token = RequestDataExtractor.getHeader(request, "token");
        //校验token
        DecodedJWT decodedJWT = null;
        try {
            decodedJWT = Sign.verifySessionToken(token, "secret");
        } catch (TokenExpiredException e) {
            return R.error(401, "登录状态已失效，请重新登录");
        } catch (Exception e) {
            return R.error(401, "您还未登录哟");
        }
        String userNoParse = decodedJWT.getClaims().get(Sign.CLAIM_USER_NO).asString();
        if (!StringUtils.equals(userNoParse,userNo)) {
            return R.error(500, "token解析错误！");
        }
        return R.ok();
    }

    /**
     * 列表
     */
    @RequestMapping("/actual/quota")
    @ApiOperation("实时指标数据获取")
    public R actualQuota(@RequestParam("deviceCode") String deviceCode){
        VtxHttpRequest request = new VtxHttpRequest();
        request.withHttpMethod(HttpMethodName.GET);
        request.withParams("deviceIds", new String[]{deviceCode});
        try {
            request.withUri(new URI(DeviceFinal.device_url));
            VtxHttpClient client = new VtxHttpClient(DeviceFinal.credential, new VtxV1Signer(), Protocol.HTTP);
            String result = client.execute(request);
            JSONObject jsonObject = JSONObject.parseObject(result);
            JSONArray jsonArrayResult1 = new JSONArray();
            JSONObject jsonObject1;
            JSONArray jsonArray = jsonObject.getJSONObject("ret").getJSONArray(deviceCode);
            for (int i = 0; i < jsonArray.size(); i++) {
                jsonObject1 = new JSONObject();
                jsonObject1.put("name",DeviceFinal.allQuotaMap.get(jsonArray.getJSONObject(i).getString("code")));
                jsonObject1.put("value",jsonArray.getJSONObject(i).getString("value"));
                jsonArrayResult1.add(jsonObject1);
            }
            return R.ok().put("quotalist", jsonArrayResult1);
        }catch(Exception e){
            e.printStackTrace();
        }
        return R.error("获取实时指标失败！");
    }



    /**
     * 附件上传到服务器
     *
     * @throws Exception
     */
    @PostMapping(value = "/uploadFile")
    @ApiOperation("附件上传到服务器")
    public R testUpLoadOss(@RequestParam("file") MultipartFile file) {
        String fileName = null;
        logger.info("name:" + file.getOriginalFilename() + ",fileName:" + file.getName());
        try {
            // 将附件保存到服务器本地
            if (file.isEmpty()) {
                return R.error();
            }
            fileName = file.getOriginalFilename();
            File dest = new File(new File(docBase).getAbsolutePath()+ "/" + fileName);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                file.transferTo(dest); // 保存文件
                return R.error();
            } catch (Exception e) {
                e.printStackTrace();
                return R.error();
            }
        } catch (Exception ex) {
            logger.error("附件上传失败！", ex);
            return R.error("附件上传失败！");
        }finally {
            Map<String, String> map = Maps.newHashMap();
            map.put("url",filePath+fileName);
            return R.ok().put("url",map);
        }
    }

    private EnterpriseThirdBindRecordEntity process (Map map) {
        EnterpriseThirdBindRecordEntity userDto = new EnterpriseThirdBindRecordEntity();
        JSONObject jsonObject = new JSONObject(map);
        if (jsonObject.getBoolean("success")) {
            JSONObject dataJsonObject = jsonObject.getJSONObject("data");
            String deptId = dataJsonObject.getString("deptId");
            if (StringUtils.isBlank(deptId)) {
                return userDto;
            }
            userDto.setDeptId(deptId);
            EnterpriseMainEntity enterpriseMainEntity = enterpriseMainService.getByDeptid(Integer.parseInt(deptId));
            if (enterpriseMainEntity != null) {
                userDto.setEnterpriseId(enterpriseMainEntity.getId());
            }
            JSONObject deptJsonObj = dataJsonObject.getJSONObject("dept");
            if (!deptJsonObj.isEmpty()) {
                userDto.setDeptName(deptJsonObj.getString("deptName"));
            }
            JSONObject originalUserObj = dataJsonObject.getJSONObject("originalUser");
            int userId = originalUserObj.getIntValue("userId");
            String userNo = originalUserObj.getString("userNo");
            String userName = originalUserObj.getString("userName");
            userDto.setUserId(userId);
            userDto.setUserNo(userNo);
            userDto.setUserName(userName);
            JSONArray roleArray = dataJsonObject.getJSONArray("roles");
            if (!roleArray.isEmpty()) {
                JSONObject firstRoleObj = roleArray.getJSONObject(0);
                String roleCode = firstRoleObj.getString("code");
                String roleName = firstRoleObj.getString("name");
                userDto.setRoleCode(roleCode);
                userDto.setRoleName(roleName);
            }
        }
        return userDto;
    }

    public WxMaService getWxMaService() {
        return wxMaService;
    }

    public EnterpriseThirdBindRecordService getEnterpriseThirdBindRecordService() {
        return enterpriseThirdBindRecordService;
    }

    /**
     * 获取忽略证书验证的client
     *
     * @return
     * @throws Exception
     */

    private CloseableHttpClient getIgnoeSSLClient() throws Exception {
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return true;
            }
        }).build();
        //创建httpClient
        CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).
                setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
        return client;
    }
}
